#include "bsp_i2c.h"

void I2C_Config(void)
{
    /* 初始化时钟 */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);

    /* 初始化GPIO */
    GPIO_InitTypeDef GPIO_InitStructure     = {0};
    GPIO_InitStructure.GPIO_Mode            = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Pin             = I2C_SCL_Pin | I2C_SDA_Pin;
    GPIO_InitStructure.GPIO_Speed           = GPIO_High_Speed;
    GPIO_InitStructure.GPIO_OType           = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd            = GPIO_PuPd_NOPULL;
    GPIO_Init(I2C_GPIO, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin             = I2C2_SCL_Pin | I2C2_SDA_Pin;
    GPIO_Init(I2C2_GPIO, &GPIO_InitStructure);

    /* 配置引脚复用映射 */
    GPIO_PinAFConfig(I2C_GPIO, GPIO_PinSource6, GPIO_AF_I2C1);  // SCL
    GPIO_PinAFConfig(I2C_GPIO, GPIO_PinSource7, GPIO_AF_I2C1);  // SDA
    GPIO_PinAFConfig(I2C2_GPIO, GPIO_PinSource10, GPIO_AF_I2C2);  // SCL
    GPIO_PinAFConfig(I2C2_GPIO, GPIO_PinSource11, GPIO_AF_I2C2);  // SDA

    /* 初始化I2C */
    I2C_InitTypeDef I2C_InitStructure = {0};
    I2C_InitStructure.I2C_Mode                = I2C_Mode_I2C;
    I2C_InitStructure.I2C_ClockSpeed          = I2C_CLK_Speed;  // 时钟速度
    I2C_InitStructure.I2C_DutyCycle           = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_Ack                 = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_OwnAddress1         = I2C_HOST_ADDRESS1; // 主机地址
    I2C_Init(I2C_x, &I2C_InitStructure);
    I2C_Init(I2C_y, &I2C_InitStructure);

    /* 开启I2C */
    I2C_Cmd(I2C_x, ENABLE);
    I2C_Cmd(I2C_y, ENABLE);
}

/**
 * @brief 等待I2C事件，超时则退出，防止陷入延时阻塞
 * 
 * @param I2Cx 使用的I2C
 * @param I2C_EVENT 等待的I2C事件
 * @return uint8_t 
 */
static uint8_t I2C_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
    uint32_t Timeout = 20000;

    while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) // 检查事件标志是否置位
    {
        Timeout --;
        if (Timeout == 0) {
            return 0;
        }
    }
    return 1;
}

/**
 * @brief 通过I2C发送数据
 * 
 * @param I2Cx 使用的I2C
 * @param slaveAddr 原始从机地址，不包含读写位
 * @param regAddr 寄存器地址
 * @param buff 要发送的数据的首地址
 * @param len 要发送的数据个数
 */
void I2Cx_SendBuff(I2C_TypeDef* I2Cx, uint8_t slaveAddr, uint8_t regAddr, uint8_t* buff, uint8_t len)
{
    /* 起始信号 */
    I2C_GenerateSTART(I2Cx, ENABLE);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT);

    /* 发送从机地址 */
    I2C_Send7bitAddress(I2Cx, slaveAddr, I2C_Direction_Transmitter);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

    /* 发送寄存器地址 */
    I2C_SendData(I2Cx, regAddr);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING);

    /* 发送数据 */
    for (int i = 0; i < len; i++) {
        I2C_SendData(I2Cx, buff[i]);
        I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
    }

    /* 等待发送完成EV8_2事件 */
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED);

    /* 停止信号 */
    I2C_GenerateSTOP(I2Cx, ENABLE);
    I2C_WaitEvent(I2Cx, I2C_FLAG_STOPF);
}

/**
 * @brief 通过I2C发送一个字节数据
 * 
 * @param I2Cx 使用的I2C
 * @param slaveAddr 原始从机地址，不包含读写位
 * @param regAddr 寄存器地址
 * @param byte 要发送的数据
 */
void I2Cx_SendByte(I2C_TypeDef* I2Cx, uint8_t slaveAddr, uint8_t regAddr, uint8_t byte)
{
    /* 起始信号 */
    I2C_GenerateSTART(I2Cx, ENABLE);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT);

    /* 发送从机地址 */
    I2C_Send7bitAddress(I2Cx, slaveAddr, I2C_Direction_Transmitter);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

    /* 发送寄存器地址 */
    I2C_SendData(I2Cx, regAddr);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING);

    /* 发送数据 */
    I2C_SendData(I2Cx, byte);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING);

    /* 等待发送完成EV8_2事件 */
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED);

    /* 停止信号 */
    I2C_GenerateSTOP(I2Cx, ENABLE);
    I2C_WaitEvent(I2Cx, I2C_FLAG_STOPF);
}

/**
 * @brief 通过I2C读取数据
 * 
 * @param I2Cx 使用的I2C
 * @param slaveAddr 原始从机地址，不包含读写位
 * @param regAddr 寄存器地址
 * @param buff 读取后缓存数组首地址
 * @param len 要读取的数据字节个数
 */
void I2Cx_ReadBuff(I2C_TypeDef* I2Cx, uint8_t slaveAddr, uint8_t regAddr, uint8_t* buff, uint8_t len)
{
    /* 起始信号 */
    I2C_GenerateSTART(I2Cx, ENABLE);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT);

    /* 发送从机地址 */
    I2C_Send7bitAddress(I2Cx, slaveAddr, I2C_Direction_Transmitter);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

    /* 发送寄存器地址 */
    I2C_SendData(I2Cx, regAddr);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING);

    /* 重新发送起始信号，切换到接收模式 */
    I2C_GenerateSTART(I2Cx, ENABLE);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT);

    /* 发送从机地址，以接收模式 */
    I2C_Send7bitAddress(I2Cx, slaveAddr+1, I2C_Direction_Receiver);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);

    for (int i = 0; i < len; i++) {
        if (i == len - 1) {           // 接收到最后一个字节的数据之前要提前发送非应答和停止信号
            I2C_AcknowledgeConfig(I2Cx, DISABLE);
            I2C_GenerateSTOP(I2Cx, ENABLE);         /* 停止信号 */
        }
        I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED);
        buff[i] = I2C_ReceiveData(I2Cx);
    }
    I2C_AcknowledgeConfig(I2Cx, ENABLE);            // 恢复默认的应答位
}

/**
 * @brief 通过I2C读取一个字节数据
 * 
 * @param I2Cx 使用的I2C
 * @param slaveAddr 原始从机地址，不包含读写位
 * @param regAddr 寄存器地址
 * @return uint8_t 返回读取的字节
 */
uint8_t I2Cx_ReadByte(I2C_TypeDef* I2Cx, uint8_t slaveAddr, uint8_t regAddr)
{
    uint8_t data = 0;

    /* 起始信号 */
    I2C_GenerateSTART(I2Cx, ENABLE);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT);

    /* 发送从机地址 */
    I2C_Send7bitAddress(I2Cx, slaveAddr, I2C_Direction_Transmitter);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

    /* 发送寄存器地址 */
    I2C_SendData(I2Cx, regAddr);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING);

    /* 重新发送起始信号，切换到接收模式 */
    I2C_GenerateSTART(I2Cx, ENABLE);
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT);

    /* 发送从机地址，以接收模式 */
    I2C_Send7bitAddress(I2Cx, slaveAddr+1, I2C_Direction_Receiver);// 从机地址加一表示接收模式
    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);

    I2C_AcknowledgeConfig(I2Cx, DISABLE);   // 接收数据后发送非应答信号
    I2C_GenerateSTOP(I2Cx, ENABLE);         // 产生停止信号

    I2C_WaitEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED);
    data = I2C_ReceiveData(I2Cx);

    I2C_AcknowledgeConfig(I2Cx, ENABLE);    // 恢复默认的应答位
    return data;
}
